/** @file cinvocator.c
 * @brief Source File creato per gestire la registrazione e l'escuzione dei comandi lanciati dal Client.
 *
 * Nell'esecuzione è responsabile di:
 *      - registrazione dei possibili comandi da lanciare
 *      - memorizzazione e centralizzazione delle richieste ricevute
 *      - controllo dello stato del programma
 *
 * @date Feb 26, 2012
 * @author Francesco Corvino
 */

#include "cinvocator.h"
#include "../logproxy/logproxy.h"

static Invoker objInvoker;

DescCommand * _invoke_getCMD(char *name){
	if(objInvoker.getCMD != NULL)
		return (*objInvoker.getCMD) (name);
	return NULL;
}

void _invoke_addReq(DescRequest *req){
	if(objInvoker.addReq!=NULL)
		objInvoker.addReq(req);
}

/** @brief Funzione di utilità per la definizione dei comandi.
 *
 * Permette di inizializzare con i valori strettamente necessari una specifica di comando.
 * @return restituisce un puntatore a una struttura descrittiva di un comando da utilizzare per registrarlo.
 * @see invoke_defineCompleteCMD() versione completa dell'inizializzazione.
 * @see registerCMD() funzione utilizzata per registrare i comandi.
 */
DescCommand * invoke_defineSimpleCMD(const char* name, void (*execute)()){
	DescCommand *tmp = malloc(sizeof(DescCommand));
	if(tmp!=NULL){
		memcpy(tmp->name, name, strlen(name));
		tmp->execute = execute;
		simplelog(
				getMultipleStrCat("definizione di un nuovo comando:",name,NULL));
	}
	return tmp;
}

/** @brief Funzione di creazione dei comandi.
 *
 * Permette di definire tramite i parametri passatti tutte le specifiche di un comando
 * rappresentato dalla struttura DescCommand.
 * @param name stringa del nome da assegnare.
 * @param setEnviroment puntatore a funzione setter per comunicare l'Enviroment, ovvero l'entità Receiver del comando.
 * @param execute puntatore a funzione che specifica il codice da eseguire su invocazione del comando.
 * @see invoke_defineSimpleCMD() versione semplificata.
 */
DescCommand * invoke_defineCompleteCMD(const char* name,
		void (*execute)(), HiddenType *info, HiddenType *type ){
	// implementata i valory base del comando
	DescCommand *tmp = invoke_defineSimpleCMD(name,execute);
	//TODOL assegna gli altri valori al comando
	if(info!=NULL) memcpy(&(tmp->descriptorInfo),info,sizeof(HiddenType));
	if(type!=NULL) memcpy(&(tmp->typeCommand),type, sizeof(HiddenType));
	return tmp;
}

/** @brief Viene incaricato di gestire inizio e terminazione della thread.
 *
 * L'invoker effettua una chiamata alla funzione manage per gestire la creazione
 * della thread, nella creazione si richiama questa funzione che incapsula
 * l'esecuzione del comando richiamato.
 *
 * @see _invoke_manageCommandLaunch()
 */
void *_invoke_incapsulationCommandLaunch(void *reqtopass){
	DescRequest *req = (DescRequest*) reqtopass;
	DescCommand *comd = req->command;
	req->tid = pthread_self();

	// TODOL aggiungere codice per monitorare il tempo di esecuzione dei comandi
	(*comd->execute)();

	simplelog("fine escuzione del comando");
    pthread_exit("Thank you for the CPU time");
    return NULL;
}

/**
 * @brief Gestisce l'invocazione di una thread per lanciare il programma.
 *
 * @param cmd puntatore al comando da lanciare
 */
void _invoke_manageCommandLaunch(DescCommand * cmd, DescRequest * req){
	pthread_t invokedcomm;
	// aggiunge i dati della richiesta in memoria
	req->command = cmd;
	// addElementToDynArray(_invoke_application.requests,req);
	_invoke_addReq(req);
	// avvia la thread con il comando
	int res = pthread_create( &invokedcomm, NULL,
			_invoke_incapsulationCommandLaunch,(void*) req);
	if(res==-1) simplelog("errore nell'invocazione del comando");
	//TODOH gestire il dopo invocazione thread

}

/**
 * @brief Setter delle configurazioni dell'invoker.
 */
void Invoke_setInvoker(Invoker *value){
	objInvoker.addReq = value->addReq;
	objInvoker.getCMD = value->getCMD;
	objInvoker.log = value->log;
}

/** \brief Funzione principale dell'entità invoker.
 * Permette di eseguire i comandi partendo da un oggetto DescRequest che caratterizza
 * il tipo di richiesta effettuata dall'utente.
 * @param req puntatore a DescRequest
 * @see DescRequest
 */
void invokeCMD(DescRequest * req){
	simplelog("ricava il command da eseguire");
	DescCommand *comm = _invoke_getCMD(req->nameCmd);
	if(comm==NULL) {
		simplelog("comando non trovato");
		return;
	}
	simplelog("avvio registrazione del comando");
	//TODO da implementare se è necessario eseguire funzioni history & undo
	simplelog("invocazione del comando");
	_invoke_manageCommandLaunch(comm,req);

}
